//译码(inst指令decode译码，缩写为id)阶段解析指令
//--xuezhen编码原版--rexiyz 20210686 增加中文注释、输出汉化逻辑

`include "defines.v"

module id_stage(
  input wire rst,
  input wire [31 : 0]inst,
  input wire [`REG_BUS]rs1_data,
  input wire [`REG_BUS]rs2_data,
  
  
  output wire rs1_r_ena,
  output wire [4 : 0]rs1_r_addr,
  output wire rs2_r_ena,
  output wire [4 : 0]rs2_r_addr,
  output wire rd_w_ena,
  output wire [4 : 0]rd_w_addr,
  
  output wire [4 : 0]inst_type,
  output wire [7 : 0]inst_opcode,//8位的指令opcode是二进制机器指令的操作码助记符，如MOV、ADD这些汇编指令都是操作码的指令助记符
  output wire [`REG_BUS]op1,
  output wire [`REG_BUS]op2
); 
// I-type
wire [6  : 0]opcode;//7位宽的指令opcode是二进制机器指令的操作码助记符，如MOV、ADD这些汇编指令都是操作码的指令助记符
wire [4  : 0]rd;//5位宽，rd存放的是目标寄存器的地址
wire [2  : 0]func3;
wire [4  : 0]rs1;//5位宽，rs1是源寄存器的地址
wire [11 : 0]imm;//12位宽，imm存放立即数，立即数通常是指在立即寻址方式指令中给出的数。立即寻址方式所提供的操作数只能用于源操作数字段，不能用于目的操作数字段，经常用于给寄存器赋值。
assign opcode = inst[6  :  0];//7位的指令opcode是二进制机器指令的操作码助记符，如MOV、ADD这些汇编指令都是操作码的指令助记符            
//always $monitor("monitor opcode:",opcode);//系统任务$monitor和$dunpvars不能跟踪它们。//always $dumpvars("dumpvars opcode:",opcode);//always $display("dumpvars opcode end"); //上面的display输出变量好像在display end后面才输出
assign rd     = inst[11 :  7];
assign func3  = inst[14 : 12];
assign rs1    = inst[19 : 15];
assign imm    = inst[31 : 20];//12位宽，imm存放立即数，立即数通常是指在立即寻址方式指令中给出的数。立即寻址方式所提供的操作数只能用于源操作数字段，不能用于目的操作数字段，经常用于给寄存器赋值。
always $displayb("\t\t\t\t\t\t\t\tdisplayb出来的可能获取不正确需要修改verilog的 inst机器指令、cpu指令二进制:",inst);//always $display("display begin");  //initial语句，该语句只执行一次；always语句，该语句循环执行若干次。在这两种语句内只能使用寄存器类型的数据，其特点是在被赋新值前保持原来的值不变。所有的initial语句和always语句在0时刻并行执行。 
always $displayb("\t\t\t\t\t\t\t\tdisplayb出来的可能获取不正确需要修改verilog的 opcode二进制机器指令的操作码助记符，如MOV、ADD这些汇编指令都是操作码的指令助记符:",opcode);
always $displayb("\t\t\t\t\t\t\t\tdisplayb出来的可能获取不正确需要修改verilog的 rd目标寄存器的地址二进制:",rd);
always $displayb("\t\t\t\t\t\t\t\tdisplayb出来的可能获取不正确需要修改verilog的 func3二进制:",func3);
always $displayb("\t\t\t\t\t\t\t\tdisplayb出来的可能获取不正确需要修改verilog的 rs1源寄存器的地址二进制:",rs1);
always $displayb("\t\t\t\t\t\t\t\tdisplayb出来的可能获取不正确需要修改verilog的 imm立即数二进制:",imm);
//开始判读是不是addi指令，RV32I指令即I型指令的opcode[0]和opcode[1]都是1所以不用判断，addi的opcode是0010011，即opcode[2]、opcode[3]、opcode[5]、opcode[6]必须是反0为1才能让ins_addi是1，而func3是000，所以把func3三位都反0成1才能让ins_addi是1
wire inst_addi = ~opcode[2] & ~opcode[3] & opcode[4] & ~opcode[5] & ~opcode[6]
                 & ~func3[0] & ~func3[1] & ~func3[2];//&如果存在位值为0的位，则结果为0，其它补充阅读在下面详解
//开始判读是不是jal指令，RV32I指令即I型指令的opcode[0]和opcode[1]都是1所以不用判断，jal的opcode是1101111
wire inst_jal  = opcode[2] & opcode[3] & ~opcode[4] & opcode[5] & opcode[6];
                 //&如果存在位值为0的位，则结果为0。如果存在位值为x（未知）或z（高阻、高阻值、悬空的一根线或引脚）的位，则结果为x。其他情况，结果为1。在verilog里x（X），表示未知状态，对大小写不敏感；z（Z），表示高阻值状态，就是电阻值高，对大小写不敏感，高阻的理想状态就是说这线完全断了，和外界没连接了，假如2～2.5V的电压被认为是逻辑1，低于0.1V的电压是逻辑0。那么高阻就是0伏，高阻很有可能会被认为是逻辑0，根据某书上说CPU只有01，高阻会被当成0处理。高阻的意义就是当多个输出同时接到一根线的时候，高阻输出电压为0伏，这样他就不会影响其他的输出信号了。其实逻辑不止0、1两个值。Verilog里面定义了4个值的逻辑0、1、高阻和X，X就是未定态。就是不知道是什么状态，有可能处于0、1定义的电平值之外的一个电平。而VHDL是9值逻辑，还有什么强0、强1，弱0，弱1之类的。VHDL中的std_logic类型，共有9个值，分别为‘U’、‘X’、‘0’、‘1’、‘Z’、‘W’、‘L’、‘H’、’-’;而Verilog中为四值逻辑，即1、0、X、Z对于FPGA来说，VHDL中的9值逻辑中 也只有’X’、‘0’、‘1’、'Z’是有意义的。因此这两种语言在逻辑值上是对等的。至于std_logic中其他一些描述非FPGA的电路情况，例如电平强、弱，Verilog中也有其定义的八种信号强度来对应，这八种强度由强到弱排列如下：supply、strong 、pull、large、weak、medium、small、highz；此时，若如果两个具有不同强度的信号驱动同一个线网，则竞争结果值为高强度信号的值；如果两个强度相同的信号之间发生竞争，则结果为不确定值。由于在FPGA中，我们并用不到这些描述，所以这里我们就不多做讨论。std_logic和std_logic_vector:它们是ieee 1164标准中引入的8逻辑值系统。不同于bit 数据类型，它可以取'0','1'，不定态和高阻态等8种不同的值。
                 //'X' “强” 不确定值（综合后为不确定值）；
                 //'0' “强” 0（综合后为0）；
                 //'1' “强” 1（综合后为1）；
                 //'Z' 高阻态（综合后为三态缓冲器）；
                 //'w' “弱” 不确定值；
                 //'L' “弱” 0；
                 //'H' “弱” 1；
                 //'-' 不可能出现的情况；
                 //std_ulogic和std_ulogic_vector:是ieee 1164标准中定义的具有9种逻辑值数据类型。std_logic类型是std_ulogic类型的一个子集。后者引入了一个新的逻辑值'U',它代表初始不定值。与std_logic不同的是，std_ulogic中没有指定两个std_ulogic信号连接到同一个节点上发生冲突后的逻辑值，因此要避免两个输入信号进行直接连接。在确保两根输出线不会连接到一起的条件下，这种9个逻辑值系统可以用来检测设计时可能发生的错误。


always $displayb("\t\t\t\t\t\t\t\tdisplayb出来的可能获取不正确需要修改verilog的 inst_addi二进制:",inst_addi);
// arith inst: 10000; logic: 01000;
// load-store: 00100; j: 00010;  sys: 000001
assign inst_type[4] = ( rst == 1'b1 ) ? 0 : inst_addi;

assign inst_opcode[0] = (  rst == 1'b1 ) ? 0 : inst_addi;
assign inst_opcode[1] = (  rst == 1'b1 ) ? 0 : 0;
assign inst_opcode[2] = (  rst == 1'b1 ) ? 0 : 0;
assign inst_opcode[3] = (  rst == 1'b1 ) ? 0 : 0;
assign inst_opcode[4] = (  rst == 1'b1 ) ? 0 : inst_addi;
assign inst_opcode[5] = (  rst == 1'b1 ) ? 0 : 0;
assign inst_opcode[6] = (  rst == 1'b1 ) ? 0 : 0;
assign inst_opcode[7] = (  rst == 1'b1 ) ? 0 : 0;




assign rs1_r_ena  = ( rst == 1'b1 ) ? 0 : inst_type[4];
assign rs1_r_addr = ( rst == 1'b1 ) ? 0 : ( inst_type[4] == 1'b1 ? rs1 : 0 );
assign rs2_r_ena  = 0;
assign rs2_r_addr = 0;

assign rd_w_ena   = ( rst == 1'b1 ) ? 0 : inst_type[4];
assign rd_w_addr  = ( rst == 1'b1 ) ? 0 : ( inst_type[4] == 1'b1 ? rd  : 0 );

assign op1 = ( rst == 1'b1 ) ? 0 : ( inst_type[4] == 1'b1 ? rs1_data : 0 );
assign op2 = ( rst == 1'b1 ) ? 0 : ( inst_type[4] == 1'b1 ? { {52{imm[11]}}, imm } : 0 );
always $displayb("\t\t\t\t\t\t\t\tdisplayb出来的可能获取不正确需要修改verilog的 op1立即数二进制:",op1);
always $displayb("\t\t\t\t\t\t\t\tdisplayb出来的可能获取不正确需要修改verilog的 op2立即数二进制:",op2);
endmodule
